
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       rcprotocol_backend.h
  * @author     baiyang
  * @date       2022-6-11
  ******************************************************************************
  */

#pragma once

#ifdef __cplusplus
extern "C"{
#endif

/*----------------------------------include-----------------------------------*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>

#include <rtthread.h>

#include <common/utility/sparse-endian.h>
/*-----------------------------------macro------------------------------------*/
#define MAX_RCIN_CHANNELS 18
#define MIN_RCIN_CHANNELS  5

#ifndef AP_RCPROTOCOL_FASTSBUS_ENABLED
  #ifdef IOMCU_FW
    #define AP_RCPROTOCOL_FASTSBUS_ENABLED 0
  #else
    #define AP_RCPROTOCOL_FASTSBUS_ENABLED 1
  #endif
#endif
/*----------------------------------typedef-----------------------------------*/
enum rcprotocol_t {
    RCP_PPM        =  0,
    RCP_IBUS       =  1,
    RCP_SBUS       =  2,
    RCP_SBUS_NI    =  3,
    RCP_DSM        =  4,
    RCP_SUMD       =  5,
    RCP_SRXL       =  6,
    RCP_SRXL2      =  7,
    RCP_CRSF       =  8,
    RCP_ST24       =  9,
    RCP_FPORT      = 10,
    RCP_FPORT2     = 11,
#if AP_RCPROTOCOL_FASTSBUS_ENABLED
    RCP_FASTSBUS   = 12,
#endif
    RCP_NONE    //last enum always is None
};

enum {
    PARSE_TYPE_SIGREAD,
    PARSE_TYPE_SERIAL
};

#pragma pack(1)
struct Channels11Bit_8Chan {
#if __BYTE_ORDER != __LITTLE_ENDIAN
#error "Only supported on little-endian architectures"
#endif
    uint32_t ch0 : 11;
    uint32_t ch1 : 11;
    uint32_t ch2 : 11;
    uint32_t ch3 : 11;
    uint32_t ch4 : 11;
    uint32_t ch5 : 11;
    uint32_t ch6 : 11;
    uint32_t ch7 : 11;
};
#pragma pack()

typedef struct rcprotocol_backend* rcprotocol_backend_t;
typedef struct rcprotocol_backend_ops* rcprotocol_backend_ops_t;

/** @ 
  * @brief  
  */
struct rcprotocol_backend {
    rcprotocol_backend_ops_t ops;

    uint32_t rc_input_count;
    uint32_t last_rc_input_count;
    uint32_t rc_frame_count;

    uint16_t _pwm_values[MAX_RCIN_CHANNELS];
    uint8_t  _num_channels;
    int16_t rssi;
    int16_t rx_link_quality;
};

/**
 * rc protocol operators
 */
struct rcprotocol_backend_ops {
    void (*rcprotocol_backend_destructor)(rcprotocol_backend_t backend);
    void (*process_pulse)(rcprotocol_backend_t backend, uint32_t width_s0, uint32_t width_s1);
    void (*process_byte)(rcprotocol_backend_t backend, uint8_t byte, uint32_t baudrate);
    void (*process_handshake)(rcprotocol_backend_t backend, uint32_t baudrate);

    // support for receivers that have FC initiated bind support
    void (*start_bind)(rcprotocol_backend_t backend);

    // allow for backends that need regular polling
    void (*update)(rcprotocol_backend_t backend);
};

/*----------------------------------variable----------------------------------*/

/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
void rcprotocol_backend_ctor(rcprotocol_backend_t backend, rcprotocol_backend_ops_t ops);

uint16_t rcprotocol_backend_read(rcprotocol_backend_t backend, uint8_t chan);
void rcprotocol_backend_read2(rcprotocol_backend_t backend, uint16_t *pwm, uint8_t n);
bool rcprotocol_backend_new_input(rcprotocol_backend_t backend);
uint8_t rcprotocol_backend_num_channels(rcprotocol_backend_t backend);
void rcprotocol_backend_add_input(rcprotocol_backend_t backend, uint8_t num_values, uint16_t *values, bool in_failsafe, int16_t _rssi, int16_t _rx_link_quality);
void rcprotocol_backend_decode_11bit_channels(const uint8_t* data, uint8_t nchannels, uint16_t *values, uint16_t mult, uint16_t div, uint16_t offset);
void rcprotocol_backend_log_data(enum rcprotocol_t prot, uint32_t timestamp, const uint8_t *data, uint8_t len);

static inline void rcprotocol_backend_destructor(rcprotocol_backend_t backend) {
    if (backend->ops->rcprotocol_backend_destructor != NULL) {
        backend->ops->rcprotocol_backend_destructor(backend);
    }
}
static inline void rcprotocol_backend_process_pulse(rcprotocol_backend_t backend, uint32_t width_s0, uint32_t width_s1) {
    if (backend->ops->process_pulse != NULL) {
        backend->ops->process_pulse(backend, width_s0, width_s1);
    }
}

static inline void rcprotocol_backend_process_byte(rcprotocol_backend_t backend, uint8_t byte, uint32_t baudrate) {
    if (backend->ops->process_byte != NULL) {
        backend->ops->process_byte(backend, byte, baudrate);
    }
}

static inline void rcprotocol_backend_process_handshake(rcprotocol_backend_t backend, uint32_t baudrate) {
    if (backend->ops->process_handshake != NULL) {
        backend->ops->process_handshake(backend, baudrate);
    }
}

// support for receivers that have FC initiated bind support
static inline void rcprotocol_backend_start_bind(rcprotocol_backend_t backend) {
    if (backend->ops->start_bind != NULL) {
        backend->ops->start_bind(backend);
    }
}

// allow for backends that need regular polling
static inline void rcprotocol_backend_update(rcprotocol_backend_t backend) {
    if (backend->ops->update != NULL) {
        backend->ops->update(backend);
    }
}

// get number of frames, ignoring failsafe
static inline uint32_t rcprotocol_backend_get_rc_frame_count(rcprotocol_backend_t backend) {
    return backend->rc_frame_count;
}

// reset valid rc frame count
static inline void rcprotocol_backend_reset_rc_frame_count(rcprotocol_backend_t backend) {
    backend->rc_frame_count = 0;
}

// get number of frames, honoring failsafe
static inline uint32_t rcprotocol_backend_get_rc_input_count(rcprotocol_backend_t backend) {
    return backend->rc_input_count;
}

// get RSSI
static inline int16_t rcprotocol_backend_get_RSSI(rcprotocol_backend_t backend) {
    return backend->rssi;
}
static inline int16_t rcprotocol_backend_get_rx_link_quality(rcprotocol_backend_t backend) {
    return backend->rx_link_quality;
}

uint32_t rcprotocol_backend_get_rc_protocols_mask(void);

// get UART for RCIN, if available. This will return false if we
// aren't getting the active RC input protocol via the uart
rt_device_t rcprotocol_backend_get_UART(void);

// get an available uart regardless of whether we have detected a protocol via it
rt_device_t rcprotocol_backend_get_available_UART(void);

// return true if we have a uart available for protocol handling.
bool rcprotocol_backend_have_UART(void);

/*------------------------------------test------------------------------------*/

#ifdef __cplusplus
}
#endif



